﻿using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using PlanningGIS.ArcGIS;

namespace PlanningGIS.Product
{
    /// <summary>
    /// 名称: 数据结构
    /// 作用：数据结构相关类（支持序列化）
    /// 作者：徐红波
    /// 日期：2012-11-01
    /// </summary>
    #region 数据结构基类
    [Serializable]
    public abstract class DataStructure
    {
        [XmlAttribute("数据结构名称")]
        public PlanningGISDataStructureType dataStructureType;

        public string DataStructureTypeToString()
        {
            switch (dataStructureType)
            {
                case PlanningGISDataStructureType.FEATURE: return "要素";
                case PlanningGISDataStructureType.FIELD: return "字段";
                case PlanningGISDataStructureType.FEATURECLASS: return "要素类";
                case PlanningGISDataStructureType.GEODATABASE: return "空间数据库";
            }

            return "";
        }
    }
    #endregion 数据结构基类

    #region 几何体
    /// <summary>
    /// 几何体
    /// </summary>
    [Serializable]
    public class PlanningGISGeometry : DataStructure
    {
        private IGeometry geometry;

        /// <summary>
        /// 几何体
        /// </summary>
        public IGeometry Geometry
        {
            get { return geometry; }
            set { geometry = value; }
        }

        /// <summary>
        /// 几何体定义字符串
        /// </summary>
        public string GeometryString
        {
            get { return GeometryToString(geometry); }
            set { geometry = StringToGeometry(value); }
        }

        /// <summary>
        /// 将几何体定义转变为字符串(坐标串)
        /// </summary>
        /// <param name="pGeometry"></param>
        /// <returns></returns>
        static public string GeometryToString(IGeometry pGeometry)
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlElement v_geoXmlElement = xmlDoc.CreateElement("data");
            XmlElement lv_geoXmlElement = null;
            XmlElement lv_geoXmlElement2 = null;
            //XmlElement lv_geoXmlElement3 = null;
            XmlText lv_text = null;

            if (pGeometry == null || pGeometry.IsEmpty) { return null; }

            string lv_GeoType = "";
            IPoint lv_point = null;
            IGeometryCollection lv_segmentCol = null;
            IGeometry lv_segment = null;
            IPointCollection lv_pointCol = null;

            switch (pGeometry.GeometryType)
            {
                case esriGeometryType.esriGeometryPoint:
                    //lv_GeoType = "Point";
                    v_geoXmlElement = xmlDoc.CreateElement("Point");
                    lv_point = pGeometry as IPoint;
                    lv_text = xmlDoc.CreateTextNode(lv_point.X.ToString() + " " + lv_point.Y.ToString());
                    v_geoXmlElement.AppendChild(lv_text);
                    break;

                case esriGeometryType.esriGeometryPolyline:
                    //lv_GeoType = "Polyline";
                    v_geoXmlElement = xmlDoc.CreateElement("Polyline");
                    lv_segmentCol = pGeometry as IGeometryCollection;

                    for (int i = 0; i < lv_segmentCol.GeometryCount; i++)
                    {
                        lv_geoXmlElement = xmlDoc.CreateElement("Section");
                        //lv_geoXmlElement.InnerText = "Section";
                        lv_segment = lv_segmentCol.get_Geometry(i);
                        lv_pointCol = lv_segment as IPointCollection;

                        for (int j = 0; j < lv_pointCol.PointCount; j++)
                        {
                            lv_point = lv_pointCol.get_Point(j);
                            lv_geoXmlElement2 = xmlDoc.CreateElement("Coordinate");
                            lv_geoXmlElement2.InnerText = lv_point.X.ToString() + " " + lv_point.Y.ToString();
                            lv_geoXmlElement.AppendChild(lv_geoXmlElement2);
                        }
                        v_geoXmlElement.AppendChild(lv_geoXmlElement);
                    }
                    break;
                case esriGeometryType.esriGeometryPolygon:
                    //lv_GeoType = "Polygon";
                    v_geoXmlElement = xmlDoc.CreateElement("Polygon");
                    lv_segmentCol = pGeometry as IGeometryCollection;

                    for (int i = 0; i < lv_segmentCol.GeometryCount; i++)
                    {
                        //TODO:还需考虑内环的情况
                        lv_geoXmlElement = xmlDoc.CreateElement("Section");
                        //lv_geoXmlElement.InnerText = "Section";
                        lv_segment = lv_segmentCol.get_Geometry(i);
                        lv_pointCol = lv_segment as IPointCollection;

                        for (int j = 0; j < lv_pointCol.PointCount; j++)
                        {
                            lv_point = lv_pointCol.get_Point(j);
                            lv_geoXmlElement2 = xmlDoc.CreateElement("Coordinate");
                            lv_geoXmlElement2.InnerText = lv_point.X.ToString() + " " + lv_point.Y.ToString();
                            lv_geoXmlElement.AppendChild(lv_geoXmlElement2);
                        }
                        v_geoXmlElement.AppendChild(lv_geoXmlElement);
                    }
                    break;
                default: return "";
            }

            xmlDoc.AppendChild(v_geoXmlElement);
            //xmlDoc.Save("c:\\test.xml");


            return xmlDoc.InnerXml;
        }

        //

        /// <summary>
        /// 将字符串(坐标串)转变为几何体
        /// </summary>
        /// <param name="pGeometry"></param>
        /// <returns></returns>
        static public IGeometry StringToGeometry(string pGeometryString)
        {
            IGeometry v_resGeo = null;
            if (pGeometryString.Trim() == "") { return null; }

            try
            {
                IPoint lv_point = null;
                IGeometryCollection lv_segmentCol = null;
                IGeometry lv_segment = null;
                IPointCollection lv_pointCol = null;
                double[] lv_location = null;

                XmlElement v_geoXmlElement = null;
                XmlElement v_geoXmlElement2 = null;

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(pGeometryString);
                XmlNode root = xmlDoc.FirstChild;

                switch (root.Name)
                {
                    case "Point":
                        v_resGeo = new PointClass();
                        v_geoXmlElement2 = root.FirstChild as XmlElement;
                        lv_location = LocationStringParser(v_geoXmlElement2.InnerText);
                        if (lv_location == null) { return null; }
                        ((IPoint)v_resGeo).X = lv_location[0];
                        ((IPoint)v_resGeo).Y = lv_location[1];
                        break;
                    case "Polyline":
                        v_resGeo = new PolylineClass();
                        lv_segmentCol = v_resGeo as IGeometryCollection;

                        for (int i = 0; i < root.ChildNodes.Count; i++)
                        {
                            v_geoXmlElement = root.ChildNodes[i] as XmlElement;
                            lv_segment = new PathClass();
                            lv_pointCol = lv_segment as IPointCollection;

                            for (int j = 0; j < v_geoXmlElement.ChildNodes.Count; j++)
                            {
                                v_geoXmlElement2 = v_geoXmlElement.ChildNodes[j] as XmlElement;
                                lv_location = LocationStringParser(v_geoXmlElement2.InnerText);
                                if (lv_location == null) { return null; }
                                lv_point = new PointClass();
                                ((IPoint)lv_point).X = lv_location[0];
                                ((IPoint)lv_point).Y = lv_location[1];
                                lv_pointCol.AddPoint(lv_point);
                            }

                            lv_segmentCol.AddGeometry(lv_segment);
                        }
                        break;
                    case "Polygon":
                        v_resGeo = new PolygonClass();
                        lv_segmentCol = v_resGeo as IGeometryCollection;

                        for (int i = 0; i < root.ChildNodes.Count; i++)
                        {
                            v_geoXmlElement = root.ChildNodes[i] as XmlElement;
                            lv_segment = new RingClass();
                            lv_pointCol = lv_segment as IPointCollection;

                            for (int j = 0; j < v_geoXmlElement.ChildNodes.Count; j++)
                            {
                                v_geoXmlElement2 = v_geoXmlElement.ChildNodes[j] as XmlElement;
                                lv_location = LocationStringParser(v_geoXmlElement2.InnerText);
                                if (lv_location == null) { return null; }
                                lv_point = new PointClass();
                                ((IPoint)lv_point).X = lv_location[0];
                                ((IPoint)lv_point).Y = lv_location[1];
                                lv_pointCol.AddPoint(lv_point);
                            }

                            lv_segmentCol.AddGeometry(lv_segment);
                        }
                        break;
                    default: return null;
                }

                return v_resGeo;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        /// <summary>
        /// 坐标字符串解析
        /// </summary>
        /// <param name="pString"></param>
        /// <returns></returns>
        static protected double[] LocationStringParser(string pString)
        {
            double[] v_res = new double[2];
            string[] v_str = pString.Split(' ');
            if (v_str.Length != 2) { return null; }
            if (double.TryParse(v_str[0], out v_res[0]) == false) { return null; }
            if (double.TryParse(v_str[1], out v_res[1]) == false) { return null; }
            return v_res;
        }

        public PlanningGISGeometry()
        {
            dataStructureType = PlanningGISDataStructureType.GEOMETRY;
        }

        public PlanningGISGeometry(IGeometry pGeometry)
        {
            dataStructureType = PlanningGISDataStructureType.GEOMETRY;
            geometry = pGeometry;
        }

        public bool IsValid()
        {
            if (geometry == null) { return false; }
            if (geometry.IsEmpty == true) { return false; }

            return true;
        }

        public static PlanningGISGeometry CreateFeature(IGeometry pGeometry)
        {
            if (pGeometry == null) { return null; }

            PlanningGISGeometry geometryNew = new PlanningGISGeometry();
            geometryNew.geometry = pGeometry;
            return geometryNew;
        }
    }
    #endregion 几何体

    #region 要素
    /// <summary>
    /// 要素
    /// </summary>
    [Serializable]
    public class PlanningGISFeature : DataStructure
    {
        private IFeature feature;

        public PlanningGISFeature()
        {
            dataStructureType = PlanningGISDataStructureType.FEATURE;
        }

        public PlanningGISFeature(IFeature pFeature)
        {
            dataStructureType = PlanningGISDataStructureType.FEATURE;
            feature = pFeature;
        }

        public bool IsValid()
        {
            if (feature == null) { return false; }
            if (feature.Shape.IsEmpty == true) { return false; }

            return true;
        }

        public static PlanningGISFeature CreateFeature(IFeature pFeature)
        {
            if (pFeature == null) { return null; }

            PlanningGISFeature featureNew = new PlanningGISFeature();
            featureNew.feature = pFeature;
            return featureNew;
        }
    }
    #endregion 要素

    #region 字段
    /// <summary>
    /// 字段
    /// </summary>
    [Serializable]
    public class PlanningGISField : DataStructure
    {
        [XmlAttribute("名称")]
        public string name;

        [XmlAttribute("别名")]
        public string aliasName;

        [XmlAttribute("类型")]
        public esriFieldType fieldType;

        [XmlAttribute("长度")]
        public int length;

        [XmlAttribute("精度")]
        public int precision;

        [XmlAttribute("小数位数")]
        public int scale;

        [XmlAttribute("默认值")]
        public string defaultValue;

        [XmlAttribute("几何定义")]
        public IGeometryDef geometryDef;

        public PlanningGISField()
        {
            dataStructureType = PlanningGISDataStructureType.FIELD;
        }


        /// <summary>
        /// 生成字段（PlanningGISField）
        /// </summary>
        /// <param name="pName"></param>
        /// <param name="pAliasName"></param>
        /// <param name="pFieldType"></param>
        /// <param name="pLength"></param>
        /// <param name="pPrecision"></param>
        /// <param name="pScale"></param>
        /// <param name="pDefaultValue"></param>
        /// <returns></returns>
        public static PlanningGISField CreateField(string pName
            , string pAliasName = ""
            , esriFieldType pFieldType = esriFieldType.esriFieldTypeString
            , int pLength = 50
            , int pPrecision = 0
            , int pScale = 0
            , string pDefaultValue = "")
        {
            PlanningGISField field = new PlanningGISField();
            field.name = pName;
            field.aliasName = pAliasName;
            field.fieldType = pFieldType;
            field.defaultValue = pDefaultValue;
            field.length = pLength;
            field.scale = pScale;
            field.precision = pPrecision;

            return field;
        }

        /// <summary>
        /// 生成字段（PlanningGISField），根据IField
        /// </summary>
        /// <param name="pField"></param>
        /// <returns></returns>
        public static PlanningGISField CreateField(IField pField)
        {
            if (pField == null) { return null; }

            PlanningGISField v_field = CreateField(pField.Name
                , pField.AliasName
                , pField.Type
                , pField.Length
                , pField.Precision
                , pField.Scale
                , pField.DefaultValue.ToString());

            if (pField.Type == esriFieldType.esriFieldTypeGeometry)
            {
                v_field.geometryDef = pField.GeometryDef;
            }

            return v_field;
        }


        /// <summary>
        /// 生成字段（IField），根据PlanningGISField
        /// </summary>
        /// <param name="pField"></param>
        /// <returns></returns>
        public static IField CreateArcGISField(PlanningGISField pField)
        {
            if (pField == null) { return null; }

            IField fieldNew = new FieldClass();
            IFieldEdit2 fieldNewEdit = fieldNew as IFieldEdit2;
            fieldNewEdit.Name_2 = pField.name;
            fieldNewEdit.AliasName_2 = pField.aliasName;
            fieldNewEdit.Type_2 = pField.fieldType;
            fieldNewEdit.Length_2 = pField.length;
            fieldNewEdit.Precision_2 = pField.precision;
            fieldNewEdit.Scale_2 = pField.scale;
            fieldNewEdit.DefaultValue_2 = pField.defaultValue;
            return fieldNew;
        }
    }
    #endregion 字段

    #region 要素类
    /// <summary>
    /// 要素类
    /// </summary>
    [Serializable]
    public class PlanningGISFeatureclass : DataStructure
    {
        [XmlAttribute("名称")]
        public string name;

        [XmlAttribute("别名")]
        public string aliasName;

        [XmlAttribute("几何字段名称")]
        public string shapeFieldName;

        [XmlAttribute("要素类型")]
        public esriFeatureType featureType;

        [XmlAttribute("几何类型")]
        public esriGeometryType geometryType;

        [XmlAttribute("Z值情况")]
        public bool hasZ;

        [XmlAttribute("M值情况")]
        public bool hasM;

        [XmlArrayAttribute("属性")]
        public List<PlanningGISField> fields;

        [XmlArrayAttribute("要素")]
        public List<PlanningGISFeature> features;

        public PlanningGISFeatureclass()
        {
            dataStructureType = PlanningGISDataStructureType.FEATURECLASS;
            fields = new List<PlanningGISField>();
            features = new List<PlanningGISFeature>();
        }

        /// <summary>
        /// 查找指定字段
        /// </summary>
        /// <param name="pFieldName"></param>
        /// <returns></returns>
        public int FindField(string pFieldName)
        {
            for (int i = 0; i < fields.Count; i++)
            {
                if (fields[i].name == pFieldName)
                {
                    return i;
                }
            }

            return -1;
        }

        /// <summary>
        /// 生成要素类信息
        /// </summary>
        /// <param name="pFeatureclass"></param>
        /// <param name="pGetFieldInfo"></param>
        /// <param name="pGetFeatureInfo"></param>
        /// <returns></returns>
        public static PlanningGISFeatureclass CreateFeatureclass(IFeatureClass pFeatureclass
            , bool pGetFieldInfo = true
            , bool pGetFeatureInfo = false)
        {
            if (pFeatureclass == null) { return null; }

            IDataset dataset = pFeatureclass as IDataset;
            PlanningGISFeatureclass zjgisFeatrueclassNew;

            zjgisFeatrueclassNew = new PlanningGISFeatureclass();
            zjgisFeatrueclassNew.name = dataset.Name;
            zjgisFeatrueclassNew.aliasName = pFeatureclass.AliasName;
            zjgisFeatrueclassNew.featureType = esriFeatureType.esriFTSimple;
            zjgisFeatrueclassNew.geometryType = pFeatureclass.ShapeType;
            zjgisFeatrueclassNew.shapeFieldName = pFeatureclass.ShapeFieldName;


            if (pGetFieldInfo == true)
            {
                for (int i = 0; i < pFeatureclass.Fields.FieldCount; i++)
                {
                    if (pFeatureclass.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeGeometry)
                    {
                        IGeometryDef geoDef = pFeatureclass.Fields.get_Field(i).GeometryDef;
                        zjgisFeatrueclassNew.hasZ = geoDef.HasZ;
                        zjgisFeatrueclassNew.hasM = geoDef.HasM;
                    }

                    zjgisFeatrueclassNew.fields.Add(PlanningGISField.CreateField(pFeatureclass.Fields.get_Field(i)));
                }
            }

            if (pGetFeatureInfo == true)
            {
                // TODO:暂不处理
            }

            return zjgisFeatrueclassNew;
        }

    }
    #endregion 要素类

    #region 数据集
    /// <summary>
    /// 数据集
    /// </summary>
    [Serializable]
    public class PlanningGISDataset : DataStructure
    {
        [XmlAttribute("名称")]
        public string name;

        [XmlArrayAttribute("要素类")]
        public List<PlanningGISFeatureclass> featureclasses;

        public PlanningGISDataset()
        {
            dataStructureType = PlanningGISDataStructureType.DATASET;
        }
    }
    #endregion 数据集

    #region 空间数据库
    /// <summary>
    /// 空间数据库
    /// </summary>
    [Serializable]
    public class PlanningGISGeodatabase : DataStructure
    {
        /// <summary>
        /// 数据库名称
        /// 不包括后缀
        /// </summary>
        [XmlAttribute("名称")]
        public string name;

        [XmlAttribute("数据库类型")]
        public zjgisWorkspaceType workspaceType;

        [XmlAttribute("数据库路径")]
        public string directory;

        [XmlArrayAttribute("数据集")]
        public List<PlanningGISDataset> datasets;

        [XmlArrayAttribute("要素类")]
        public List<PlanningGISFeatureclass> featureclasses;

        public PlanningGISGeodatabase()
        {
            dataStructureType = PlanningGISDataStructureType.GEODATABASE;
            featureclasses = new List<PlanningGISFeatureclass>();
            datasets = new List<PlanningGISDataset>();
        }

        /// <summary>
        /// 获取数据库类型字符串
        /// </summary>
        /// <returns></returns>
        public string WorkspaceTypeToString()
        {
            switch (workspaceType)
            {
                case zjgisWorkspaceType.gdb: return "File Geodatabase(.gdb)";
                case zjgisWorkspaceType.mdb: return "Personal Geodatabase(.mdb)";
                case zjgisWorkspaceType.sde: return "Spatial Data Engine(.sde)";
            }

            return "";
        }

        /// <summary>
        /// 获取数据库绝对路径
        /// </summary>
        /// <returns></returns>
        public string GetFullDatabasePath()
        {
            string fullDatabasePath = directory + "\\" + name;

            switch (workspaceType)
            {
                case zjgisWorkspaceType.gdb: return fullDatabasePath += ".gdb";
                case zjgisWorkspaceType.mdb: return fullDatabasePath += ".mdb";
                case zjgisWorkspaceType.sde: return fullDatabasePath;
            }

            return fullDatabasePath;
        }
    }
    #endregion 空间数据库

    #region 数据结构类型 枚举
    /// <summary>
    /// 数据结构类型 枚举
    /// </summary>
    public enum PlanningGISDataStructureType
    {
        GEOMETRY,               // 几何体
        FEATURE,                // 要素
        FIELD,                  // 字段
        FEATURECLASS,           // 要素类
        DATASET,                // 数据集
        GEODATABASE             // 空间数据库
    }
    #endregion 数据结构类型 枚举

    #region 比例尺 枚举
    /// <summary>
    /// 数据结构类型 枚举
    /// </summary>
    public enum PlanningGISDataScale
    {
        B,              // 50万（1:500000）
        C,              // 25万（1:250000）
        D,              // 10万（1:100000）
        E,              // 5万（1:50000）
        F,              // 2万5（1:25000）
        G,              // 1万（1:10000）
        H               // 5千（1:5000）
    }
    #endregion 比例尺 枚举

    #region 数据库 枚举
    /// <summary>
    /// 数据库 枚举
    /// </summary>
    public enum PlanningGISDatabaseType
    {
        DLG,              // 基础空间要素数据库
        DEM,              // 数字高程模型数据库
        DOM,              // 数字正射影像数据库
        MD,               // 元数据数据库
        PN,               // 地名数据库
        DDK,              // 大地成果数据库
        MT,               // 系统维护数据库
        D3                // 三维景观数据库
    }
    #endregion 数据库 枚举
}
